/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "push_sinc_resampler.h"

#include <cstring>

#include "audio_util.h"

namespace webrtc {

    PushSincResampler::PushSincResampler(size_t source_frames,
                                         size_t destination_frames)
            : resampler_(new SincResampler(source_frames * 1.0 / destination_frames,
                                           source_frames,
                                           this)),
              source_ptr_(nullptr),
              source_ptr_int_(nullptr),
              destination_frames_(destination_frames),
              first_pass_(true),
              source_available_(0) {}

    PushSincResampler::~PushSincResampler() = default;

    size_t PushSincResampler::Resample(const int16_t *source,
                                       size_t source_length,
                                       int16_t *destination,
                                       size_t destination_capacity) {
        if (!float_buffer_)
            float_buffer_.reset(new float[destination_frames_]);

        source_ptr_int_ = source;
        // Pass nullptr as the float source to have Run() read from the int16 source.
        Resample(nullptr, source_length, float_buffer_.get(), destination_frames_);
        FloatS16ToS16(float_buffer_.get(), destination_frames_, destination);
        source_ptr_int_ = nullptr;
        return destination_frames_;
    }

    size_t PushSincResampler::Resample(const float *source,
                                       size_t source_length,
                                       float *destination,
                                       size_t destination_capacity) {
        RTC_CHECK_EQ(source_length, resampler_->request_frames());
        RTC_CHECK_GE(destination_capacity, destination_frames_);
        // Cache the source pointer. Calling Resample() will immediately trigger
        // the Run() callback whereupon we provide the cached value.
        source_ptr_ = source;
        source_available_ = source_length;

        // On the first pass, we call Resample() twice. During the first call, we
        // provide dummy input and discard the output. This is done to prime the
        // SincResampler buffer with the correct delay (half the kernel size), thereby
        // ensuring that all later Resample() calls will only result in one input
        // request through Run().
        //
        // If this wasn't done, SincResampler would call Run() twice on the first
        // pass, and we'd have to introduce an entire |source_frames| of delay, rather
        // than the minimum half kernel.
        //
        // It works out that ChunkSize() is exactly the amount of output we need to
        // request in order to prime the buffer with a single Run() request for
        // |source_frames|.
        if (first_pass_)
            resampler_->Resample(resampler_->ChunkSize(), destination);

        resampler_->Resample(destination_frames_, destination);
        source_ptr_ = nullptr;
        return destination_frames_;
    }

    void PushSincResampler::Run(size_t frames, float *destination) {
        // Ensure we are only asked for the available samples. This would fail if
        // Run() was triggered more than once per Resample() call.
        RTC_CHECK_EQ(source_available_, frames);

        if (first_pass_) {
            // Provide dummy input on the first pass, the output of which will be
            // discarded, as described in Resample().
            std::memset(destination, 0, frames * sizeof(*destination));
            first_pass_ = false;
            return;
        }

        if (source_ptr_) {
            std::memcpy(destination, source_ptr_, frames * sizeof(*destination));
        } else {
            for (size_t i = 0; i < frames; ++i)
                destination[i] = static_cast<float>(source_ptr_int_[i]);
        }
        source_available_ -= frames;
    }

}  // namespace webrtc
